package database.local;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.TreeMap;

import server.SaveZoneHttpConnection;

import com.google.android.maps.GeoPoint;

import android.app.Activity;
import android.content.SharedPreferences;
import android.location.Address;
import android.location.Geocoder;
import android.os.AsyncTask;
import android.preference.PreferenceManager;
import android.util.Log;
import app.ui.AndroAppActivity;
import app.ui.OpeningActivity;
import app.ui.PreferencesActivity;
import database.local.Business;
import database.local.QueryData;

public class DataBaseClient {
	private List<Business> business_list;
	private List<Business> unsync_business_lst = new ArrayList<Business>();
	private Deal unsync_deal;
	AndroAppActivity context=null;
	OpeningActivity context_o=null;
	SharedPreferences prefs;
	String facebook_user;
	String facebook_mail;
	String facebook_image;
	String facebook_image_small;
	private String facebook_page_url;

	public DataBaseClient(AndroAppActivity app) {
		business_list = null;
		context = app;
	};
	public DataBaseClient(OpeningActivity app) {
		business_list = null;
		context_o = app;
	};

	// Query the database. Optional inputs: Country, City
	// Data ("dd/mm/yy"), Time ("hh:mm"), Delta Time (in hours)
	// Example: myclass.Execute("Israel", "Tel Aviv", "25/12/11", "20:00", "1")
	// Comments: Delta=1 means from Time to Time + 1

	public class MyAsyncSyncronyser extends AsyncTask<String, Void, Integer> {
		protected Integer doInBackground(String... params) {
			Log.w("AndroApp", "Started sync with MyAsyncSyncronyser");
			if (!unsync_business_lst.isEmpty()) {
				Log.i("DataBase Client", "Have some deals to add");
				try {
					List<Business> temp_list = new ArrayList<Business>();
					temp_list.addAll(unsync_business_lst);
					updateFacebook();
					SaveZoneHttpConnection.add(temp_list, new User(
							facebook_user, facebook_mail));
					unsync_business_lst.clear();
				} catch (Exception e) {
					unsync_business_lst.clear();
					e.printStackTrace();
				}
			}

			try {
				QueryData query = new QueryData();
				if (params.length > 0) {
					query.setCountry(params[0]);
					query.setCity(params[1]);
				}
				if (params.length > 2) {
					query.setType_btm(Integer.parseInt(params[2]));
					query.setDays_bitmap(Integer.valueOf(params[3]));
					Log.i("DataClient", "Time here is: " + params[4]);
					query.setTime(params[4]);
					query.setTimeDelta(Integer.parseInt(params[5]));
				}
				// query relevant deals
				business_list = query.getData();
				if (business_list == null)
					return 0;

				Log.w("AndroApp", "Finished sync with MyAsyncSyncronyser");
				return 1;
			} catch (IOException e1) {
				Log.e("DEBUGTAG", "Remote business list exception", e1);
			}

			Log.w("AndroApp", "Finished sync with MyAsyncSyncronyser");
			return 0;
		}

		protected void onPostExecute(Integer result) {
			Log.i("AndroApp",
					"Done getting data, found " + business_list.size()
							+ " business");
			context.onEndOfData();
		}

	}
	

	public class MyAsyncRater extends AsyncTask<String, Void, Integer> {
		protected Integer doInBackground(String... params) {
			int rate = 0, cost_level = 0;
			String comment = "";
			Log.w("AndroApp", "Started sync with MyAsyncRater");
			if (unsync_deal != null) {
				Log.w("AndroApp", "Deal key = " + unsync_deal.getKey());
				try {

					for (int i = 0; i < params.length - 1; i += 2) {
						if (params[i].matches("rate")) {
							rate = Integer.parseInt(params[i + 1]);
							Log.i("MyAsyncRater", "Calling RateMe...");
						} else if (params[i].matches("money")) {
							cost_level = Integer.parseInt(params[i + 1]);
							Log.i("MyAsyncRater", "Calling costLevelMe...");
						} else if (params[i].matches("comment")) {
							comment = params[i + 1];
							Log.i("MyAsyncRater", "Calling Comments...");
						}

					}
					if (comment != "")
						updateFacebook();
						unsync_deal.commentMe(comment, rate, cost_level,
								new User(facebook_user,
										 facebook_mail));
					unsync_deal = null;
				} catch (Exception e) {
					unsync_deal = null;
					e.printStackTrace();
				}
			}

			Log.w("AndroApp", "Finished sync with MyAsyncRater");
			return 0;
		}

		protected void onPostExecute(Integer result) {
//			context.onEndOfData();
		}
	}


	public class MyAsyncChecker extends AsyncTask<String, Void, Integer> {
		protected Integer doInBackground(String... params) {
			Log.w("AndroApp", "Started sync with MyAsyncChecker");
			if (unsync_business_lst != null) {
				Log.w("AndroApp", "Business key = " + unsync_business_lst.get(0).getKey());
				try {

					updateFacebook();

					unsync_business_lst.get(0).checkInMe(facebook_user, facebook_mail,facebook_image_small, facebook_page_url);
					unsync_business_lst = null;
				} catch (Exception e) {
					unsync_business_lst = null;
					e.printStackTrace();
				}
			}

			Log.w("AndroApp", "Finished sync with MyAsyncChecker");
			return 0;
		}

		protected void onPostExecute(Integer result) {
//				context.onEndOfData();
		}
	}
	
	public class MyAsyncLiker extends AsyncTask<String, Void, Integer> {
		protected Integer doInBackground(String... params) {
			Log.w("AndroApp", "Started sync with MyAsyncLiker");
			if (unsync_business_lst != null) {
				Log.w("AndroApp", "Deal key = " + unsync_deal.getKey());
				try {

					updateFacebook();

					unsync_deal.likeMe(new User(facebook_user, facebook_mail));
					unsync_deal = null;
				} catch (Exception e) {
					unsync_deal = null;
					e.printStackTrace();
				}
			}

			Log.w("AndroApp", "Finished sync with MyAsyncChecker");
			return 0;
		}

		protected void onPostExecute(Integer result) {
//				context.onEndOfData();
		}
	}

	private void updateFacebook() {
		if (context!=null)
			prefs = PreferenceManager.getDefaultSharedPreferences(context);
		else
			prefs = PreferenceManager.getDefaultSharedPreferences(context_o);

		facebook_user = prefs.getString(
				PreferencesActivity.PREF_FACEBOOK_USER, "");
		facebook_mail = prefs.getString(
				PreferencesActivity.PREF_FACEBOOK_MAIL, "");
		facebook_image = prefs.getString(
				PreferencesActivity.PROFILE_PIC_URL, "");
		facebook_image_small = prefs.getString(
				PreferencesActivity.PROFILE_PIC_URL_SMALL, "");
		facebook_page_url = prefs.getString(
				PreferencesActivity.PREF_FACEBOOK_PAGE_URL, "");
	}	
	
	
	
	
	
	
	public class MyAsyncFucker extends AsyncTask<String, Void, Integer> {
		protected Integer doInBackground(String... params) {
			Log.w("AndroApp", "Started sync with MyAsyncFucker");
			if (unsync_deal != null) {
				Log.w("AndroApp", "Deal key = " + unsync_deal.getKey());
				try {
					updateFacebook();
					unsync_deal.fakeMe(new User(facebook_user,
							                    facebook_mail));
					unsync_deal = null;
				} catch (Exception e) {
					unsync_deal = null;
					e.printStackTrace();
				}
			}
			Log.w("AndroApp", "Finished sync with MyAsyncFucker");
			return 0;
		}

		protected void onPostExecute(Integer result) {
//			context.onEndOfData();
		}
	}

	// Query the database. Optional inputs: Country, City
	// Data ("dd/mm/yy"), Time ("hh:mm"), Delta Time (in hours)
	// Example: myclass.Execute("Israel", "Tel-Aviv", "25/12/11", "20:00", "1")
	// Comments: Delta=1 means from Time to Time + 1
	public void sync(String... params) {
		MyAsyncSyncronyser inst = new MyAsyncSyncronyser();
		try {
			inst.execute(params);
		} catch (Exception e) {
			Log.e("DEBUGTAG", "Problem with sycronization", e);
		}
	}

	/**
	 * Function rates, moneys and comments deal
	 * 
	 * @param deal
	 *            - deal to rate, null for "add deal"
	 * @param rating
	 *            - 1..5 or -1 for no rating
	 * @param money
	 *            - 1..3 or -1
	 * @param comments
	 *            - string or null
	 */
	public void rate(Deal d, int rating, int money, String comments) {
		MyAsyncRater inst = new MyAsyncRater();
		String[] params = new String[6];
		int i = 0;
		if (d != null) {
			unsync_deal = d;
		}
		try {
			if (rating != -1) {
				params[i++] = "rate";
				params[i++] = String.valueOf(rating);
			}
			if (money != -1) {
				params[i++] = "money";
				params[i++] = String.valueOf(money);
			}
			if (comments != null) {
				params[i++] = "comment";
				params[i++] = comments;
			}

			for (int ind = i; ind < 6; ind++) {
				params[ind] = "";
			}

			inst.execute(params);
		} catch (Exception e) {
			Log.e("DEBUGTAG", "Problem with sycronization", e);
		}
	}

	public void fakeDeal(Deal d) {
		MyAsyncFucker inst = new MyAsyncFucker();
		if (d != null) {
			unsync_deal = d;
		}
		try {
			inst.execute();
		} catch (Exception e) {
			Log.e("DEBUGTAG", "Problem with sycronization", e);
		}
	}
	
	public void Like(Deal d) {
		MyAsyncLiker inst = new MyAsyncLiker();
		if (d != null) {
			unsync_deal = d;
		}
		try {
			inst.execute();
		} catch (Exception e) {
			Log.e("DEBUGTAG", "Problem with sycronization", e);
		}
	}
	
	
	public void CheckIn(Business b) {
		Log.w("DataBAseClient","Starting checkin");
		MyAsyncChecker inst = new MyAsyncChecker();
		if (b != null) {
			unsync_business_lst = new ArrayList<Business>();
			unsync_business_lst.add(b);
		}
		try {
			inst.execute();
		} catch (Exception e) {
			Log.e("DEBUGTAG", "Problem with sycronization", e);
		}
	}
	
	
	public void syncGeoPoint(List<Business> lb, String... params) {
		unsync_business_lst.addAll(lb);
		MyAsyncSyncronyser inst = new MyAsyncSyncronyser();
		try {
			inst.execute();
		} catch (Exception e) {
			Log.e("DEBUGTAG", "Problem with sycronization", e);
		}
	}

	/**
	 * The method will return a list of businesses sorted by Rating
	 * 
	 * @param Type
	 *            (pub/rest/all)
	 * @return List of businesses sorted by Rating
	 */
	public List<Business> getListbyRating(GeoPoint geo_point) {

		if (business_list != null) {
			Collections.sort(business_list);
			Utils.calcDistance(business_list, geo_point);
			return this.business_list;
		} else {
			return null;
		}
	}

	public List<Business> getListbyRating(int rating_thr, int money_thr,
			GeoPoint geo_point) {
		List<Business> tempList = new LinkedList<Business>();

		for (Business b : getListbyRating(geo_point)) {
			if ((b.getDealMaxRate().getRate() >= rating_thr)
					&& (b.getDealMaxRate().getCostLevel() >= money_thr)) {
				tempList.add(b);
			}
		}

		return tempList;
	}

	/**
	 * The method will return a list of businesses sorted by Name
	 * 
	 * @param Type
	 *            (pub/rest/all)
	 * @return List of businesses sorted by Name
	 */
	public List<Business> getListbyName() {
		if (business_list == null)
			return null;
		Collections.sort(business_list, new Comparator<Business>() {
			public int compare(Business e1, Business e2) {
				return e1.getName().compareTo(e2.getName());
			}
		});
		return this.business_list;
	}

	public List<Business> getListbyName(int rating_thr, int money_thr,
			GeoPoint geo_point) {

		List<Business> tempList = new LinkedList<Business>();
		for (Business b : getListbyRating(geo_point)) {
			if ((b.getDealMaxRate().getRate() >= rating_thr)
					&& (b.getDealMaxRate().getCostLevel() >= money_thr)) {
				tempList.add(b);
			}
		}

		return tempList;
	}

	/**
	 * FIXME: irelevant code The functions maps businesses to
	 * distance_to_current_location and returns list of pairs
	 * <Distance,Business> in distance R from curPoint
	 * 
	 * @param type
	 *            - pub/rest/all
	 * @param curPoint
	 *            - current location on the map
	 * @param geoCoder
	 *            - geoCoder assigned to map view
	 * @param R
	 *            - max distance to business from curPoint
	 * @return list of <distance_to_current_location,businesses>
	 */
	public List<Entry<Integer, Business>> getListbyDistance(String type,
			GeoPoint curPoint, Geocoder geoCoder, double R) {

		NavigableMap<Integer, Business> businessMap = new TreeMap<Integer, Business>();
		List<Business> curList = new LinkedList<Business>();
		List<Entry<Integer, Business>> distList = new ArrayList<Entry<Integer, Business>>();
		double cur_dist = 0;

		curList.addAll(business_list);
		for (Business b : curList) {
			GeoPoint p;
			try {
				p = b.getGeoPoint();
				if (p == null) {
					Log.e("Close Business", "No geo point");
					List<Address> foundAddress = geoCoder.getFromLocationName(
							b.getFullAddress(), 5);
					if (foundAddress.size() > 0) {
						cur_dist = Math
								.pow(Math.pow(
										(int) (foundAddress.get(0)
												.getLatitude() * 1E6)
												- curPoint.getLatitudeE6(), 2)
										+ Math.pow((int) (foundAddress.get(0)
												.getLongitude() * 1E6)
												- curPoint.getLongitudeE6(), 2),
										0.5);
					}
				} else {
					cur_dist = Math.pow(
							Math.pow(
									(int) (p.getLatitudeE6())
											- curPoint.getLatitudeE6(), 2)
									+ Math.pow((int) (p.getLongitudeE6())
											- curPoint.getLongitudeE6(), 2),
							0.5);
				}

				businessMap.put((int) cur_dist, b);

			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		while ((businessMap.firstEntry() != null)
				&& (businessMap.firstEntry().getKey() <= R)) {
			distList.add(businessMap.pollFirstEntry());
		}

		return distList;
	};

	public List<Entry<Integer, Business>> getListbyDistance(String type,
			GeoPoint curPoint, Geocoder geoCoder, int R, int rating_thr,
			int money_thr) {

		List<Entry<Integer, Business>> tempList = new ArrayList<Entry<Integer, Business>>();

		for (Entry<Integer, Business> b : getListbyDistance(type, curPoint,
				geoCoder, R)) {
			if ((b.getValue().getDealMaxRate().getRate() < rating_thr)
					&& (b.getValue().getDealMaxRate().getCostLevel() < money_thr)) {
				tempList.add(b);
			}
		}

		return tempList;
	}

	/**
	 * 
	 * @param b
	 * @param nameDeal
	 * @param dealInfoDeal
	 * @param timeDeal
	 * @param delta_time_Deal
	 * @param days_bitmap
	 * @param cost_level
	 * @param rate
	 * @return
	 */
	public int AddDeal(Business b, String nameDeal, String dealInfoDeal,
			String timeDeal, String delta_time_Deal, int days_bitmap, int rate,
			int cost_level) {
		List<Deal> dealList = new LinkedList<Deal>();

		Log.i("DataBase Client", "Creating new deal");
		Deal d = new Deal();

		d.setName(nameDeal);
		d.setDealInfo(dealInfoDeal);
		d.setTime(timeDeal);
		d.setDelta_time(delta_time_Deal);
		d.setDays_bitmap(days_bitmap);
		d.setTo_rate(rate);
		d.setTo_cost_level(cost_level);

		dealList = b.getDeals();
		dealList.add(d);

		b.setDeals(dealList);

		unsync_business_lst.add(b);

		return 0;
	}

	/**
	 * 
	 * @param nameBusiness
	 * @param countryBusiness
	 * @param cityBusiness
	 * @param streetBusiness
	 * @param streetNumBusiness
	 * @param fullAddressBusiness
	 * @param typeBusiness
	 * @param websiteBusiness
	 * @param nameDeal
	 * @param dealInfoDeal
	 * @param timeDeal
	 * @param delta_time_Deal
	 * @param days_bitmap
	 * @return
	 */
	public int AddDeal(GeoPoint p, String nameBusiness, String countryBusiness,
			String cityBusiness, String streetBusiness, int streetNumBusiness,
			String fullAddressBusiness, String typeBusiness,
			String websiteBusiness, String dealInfoDeal, String nameDeal,
			String timeDeal, String delta_time_Deal, int days_bitmap, int rate,
			int cost_level) {

		Log.i("DataBase Client", "Creating new business");
		Business b = new Business();
		b.setName(nameBusiness);
		b.setCountry(countryBusiness);
		b.setCity(cityBusiness);
		b.setStreet(streetBusiness);
		b.setStreetNum(streetNumBusiness);
		b.setFullAddress(fullAddressBusiness);
		b.setType(typeBusiness);
		b.setWebsite(websiteBusiness);
		b.setGeoPoint(p);
		AddDeal(b, nameDeal, dealInfoDeal, timeDeal, delta_time_Deal,
				days_bitmap, rate, cost_level);

		return 0;
	}

	// public void DealRate(Deal d, int )

}
